package qy.qyalgotrader;

import java.io.IOException;
import java.nio.file.Path;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.OptionalInt;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import qy.jalgotrade.Initializer;
import qy.jalgotrade.bar.Bar.Frequency;
import qy.jalgotrade.utils.CommonUtils;
import qy.qyalgotrader.utils.CommonDataUtils;
import qy.qyalgotrader.utils.Constants;
import tech.tablesaw.api.Table;

/**
 * @author qy
 *
 */
public final class HisDataUtils {

	private static final Logger logger;

	static {
		try {
			Initializer.main(null);
		} catch (IOException e) {
			e.printStackTrace();
		}
		logger = LoggerFactory.getLogger(HisDataUtils.class);
	}

	/**
	 * 仅支持 Tick 级别 resample 到日内级别: MIN_1, MIN_3, MIN_5, MIN_15, MIN_30, MIN_60 以及 日线 day 级别:
	 * 
	 * @param  codesOrWcs     codesOrWcs
	 * @param  td             td
	 * @param  resFreq        resFreq
	 * @param  cowRegexP      default: null
	 * @param  paddingEmptyBarsP      default: false
	 * @param  baseDirP       default: CommonDataUtils.TS_HIS_DATA_BASE_DIR_DEFAULT
	 * @param  rewriteP       default: false
	 * @param  breakOnExceptP default: false
	 * @param  genEmptyFileP  default: false
	 * @param  encP           default: "UTF-8"
	 * @param  intervalP      default: "UTF-8"
	 * @throws Exception      Exception
	 */
	public static void resampleHisTickDataFileTs(List<String> codesOrWcs, LocalDate td, Frequency resFreq,
			Optional<String> cowRegexP, Optional<Boolean> paddingEmptyBarsP, Optional<Path> baseDirP, Optional<Boolean> rewriteP,
			Optional<Boolean> breakOnExceptP, Optional<Boolean> genEmptyFileP, Optional<String> encP,
			OptionalInt intervalP) throws Exception {

		String cowRegex = cowRegexP.orElse(null);
		boolean paddingEmptyBars = paddingEmptyBarsP.orElse(false);
		Path baseDir = baseDirP.orElse(CommonDataUtils.TS_HIS_DATA_BASE_DIR_DEFAULT);
		boolean rewrite = rewriteP.orElse(false);
		boolean breakOnExcept = breakOnExceptP.orElse(false);
		boolean genEmptyFile = genEmptyFileP.orElse(false);
		String enc = encP.orElse("UTF-8");
		int interval = intervalP.orElse(100);

		resampleHisTickDataFileTs(codesOrWcs, td, resFreq, cowRegex, paddingEmptyBars, baseDir, rewrite, breakOnExcept, genEmptyFile,
				enc, interval);
	}

	/**
	 * 
	 * @param codesOrWcs codesOrWcs
	 * @param td td
	 * @param resFreq resFreq
	 * @param cowRegex cowRegex
	 * @param paddingEmptyBars paddingEmptyBars
	 * @param baseDir baseDir
	 * @param rewrite rewrite
	 * @param breakOnExcept breakOnExcept
	 * @param genEmptyFile genEmptyFile
	 * @param enc enc
	 * @param interval interval
	 * @throws Exception Exception
	 */
	public static void resampleHisTickDataFileTs(List<String> codesOrWcs, LocalDate td, Frequency resFreq,
			String cowRegex, boolean paddingEmptyBars, Path baseDir, boolean rewrite, boolean breakOnExcept, boolean genEmptyFile, String enc,
			int interval)
			throws Exception {

		if (!Frequency.getIntradayFreqsCommon().contains(resFreq) && !Frequency.DAY.equals(resFreq)) {
			throw new IllegalArgumentException(
					String.format("Unsupported resample frequency (NOT intraday frequency or daily): %s", resFreq));
		}

		Path tickCsvDir = baseDir.resolve("tick").resolve(td.format(Constants.DATE_FORMAT_YYYY))
				.resolve(td.format(Constants.DATE_FORMAT_YYYYMMDD));
		Path rsmpCsvDir = baseDir.resolve(CommonDataUtils.barFreqToHisDataDir(resFreq))
				.resolve(td.format(Constants.DATE_FORMAT_YYYY)).resolve(td.format(Constants.DATE_FORMAT_YYYYMMDD));
		if (!rsmpCsvDir.toFile().exists()) {
			rsmpCsvDir.toFile().mkdirs();
		}
		// codesOrWcs 为 None 或 空集合, 则 resample 源 Tick 文件夹中所有品种:
		if (codesOrWcs == null || codesOrWcs.size() == 0) {
			codesOrWcs = CommonDataUtils.getAllCodesOrWcsFromDir(tickCsvDir, Optional.empty(), Optional.empty());
		}

		for (String e : codesOrWcs) {
			try {
				Path aRsmpCsvFile = rsmpCsvDir.resolve(e + ".csv");
				if (!aRsmpCsvFile.toFile().exists() || rewrite) {
					// 读取 tdObj 交易日所有的 tick:
					Table ticksDf = CommonDataUtils.readFromTicksCsvTs(e, td, baseDir.resolve("tick"), ".csv", enc);
					LocalDateTime startDm = LocalDateTime.of(td, Constants.OPEN_TIME_AM);
					LocalDateTime endDm = LocalDateTime.of(td, Constants.CLOSE_TIME_PM);
					// 对 Ticks 进行 sample 得到 min_1 级别 Bars:
					Table minsDf = CommonDataUtils.sampleDataFrameTicksToMinuteTs(e, td, ticksDf, startDm, endDm, paddingEmptyBars, 59);
					if (!minsDf.isEmpty() && !ticksDf.isEmpty()) {
						if (!Arrays.asList(Frequency.MINUTE, Frequency.MINUTE_1).contains(resFreq)) {
							// 前收盘 = 当日首个 Tick Price - change
							double priceRefVal = ticksDf.row(0).getDouble(Constants.PRICE)
									- ticksDf.row(0).getDouble(Constants.CHANGE);
							// resample 得到其他分钟级别 Bars:
							minsDf = CommonDataUtils.resampleDataFrameMinuteToTs(minsDf, startDm, endDm, resFreq,
									priceRefVal);
						}
					}
					// 写入 csv:
					if (!minsDf.isEmpty() || genEmptyFile) {
						CommonUtils.toCsv(minsDf, aRsmpCsvFile.toFile(), CommonUtils.CSV_FORMAT_DEFAULT, rewrite, enc);
						logger.info("resample (freq: {}) {} Ticks to {} (total: {} bars).", resFreq, e, aRsmpCsvFile,
								minsDf.rowCount());
						Thread.sleep(interval);
					}
				}
			} catch (Exception ex) {
				if (breakOnExcept) {
					throw ex;
				} else {
					String stkInfo = ExceptionUtils.getStackTrace(ex);
					logger.error(
							"exception occured in resampleHisTickDataFileTs({}, {}, {}, {}, {}, {}, {}, {}): {e: {}, stackTrace: {}}",
							codesOrWcs, td, resFreq, baseDir, rewrite, breakOnExcept, genEmptyFile, enc, ex, stkInfo);
				}
			}
		}
	}

	/**
	 * @param  args args
	 * @throws Exception Exception
	 */
	public static void main(String[] args) throws Exception {

		List<String> wcs = Arrays.asList("510050.SH", "510300.SH", "510500.SH");
		// all:
		wcs = Collections.emptyList();
//		LocalDate fromTd = LocalDate.parse("2017-01-01", Constants.DATE_FORMAT_STD);
//		LocalDate toTd = LocalDate.parse("2018-12-31", Constants.DATE_FORMAT_STD);
//		LocalDate fromTd = LocalDate.parse("2019-05-30", Constants.DATE_FORMAT_STD);
//		LocalDate toTd = LocalDate.parse("2019-06-30", Constants.DATE_FORMAT_STD);
		LocalDate fromTd = LocalDate.parse("2019-10-17", Constants.DATE_FORMAT_STD);
		LocalDate toTd = LocalDate.parse("2019-12-31", Constants.DATE_FORMAT_STD);
//		LocalDate fromTd = LocalDate.parse("2021-01-25", Constants.DATE_FORMAT_STD);
//		LocalDate toTd = LocalDate.parse("2021-02-26", Constants.DATE_FORMAT_STD);
		List<LocalDate> tds = CommonDataUtils.tradingDaysBy(fromTd, toTd);
		// @formatter:off
		List<Frequency> freqs = Arrays.asList(
				Frequency.MINUTE
//				Frequency.MINUTE_3,
//				Frequency.MINUTE_5,
//				Frequency.MINUTE_15,
//				Frequency.MINUTE_30,
//				Frequency.DAY
		);
		// @formatter:on
		for (LocalDate e : tds) {
			for (Frequency ei : freqs) {
				resampleHisTickDataFileTs(wcs, e, ei, Optional.empty(), Optional.empty(), Optional.empty(), Optional.of(false),
						Optional.empty(), Optional.empty(), Optional.empty(), OptionalInt.of(100));
			}
		}

		Initializer.shutdown();
	}

	private HisDataUtils() {

	}
}
